package cn.tqfeiyang.smartadmin.commons.logging;

import cn.tqfeiyang.smartadmin.commons.toolbox.RequestUtils;
import cn.tqfeiyang.smartadmin.commons.toolbox.ResponseUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * HTTP日志过滤器。
 *
 * 在里子中输出每个请求和响应。
 *
 * @author tqfeiyang
 * @since 2024-07-19
 */
@Slf4j
@Component
@Order(-999)
@WebFilter(urlPatterns = "/*")
public class AccessLoggingFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        ContentCachingRequestWrapper req = new ContentCachingRequestWrapper(request);
        ContentCachingResponseWrapper resp = new ContentCachingResponseWrapper(response);

        // Execution request chain
        filterChain.doFilter(req, resp);

        // Get Cache
        byte[] requestBody = req.getContentAsByteArray();
        byte[] responseBody = resp.getContentAsByteArray();

        logRequest(request, requestBody);
        logResponse(response, responseBody);

        // Finally remember to respond to the client with the cached data.
        resp.copyBodyToResponse();
    }

    private void logRequest(HttpServletRequest request, byte[] body) throws IOException
    {
        log.debug("===============================request begin========================================");
        log.debug("Request URL     : {}", request.getRequestURL());
        log.debug("Request URI     : {}", request.getRequestURI());
        log.debug("IP Address      : {}", RequestUtils.getClientIp(request));
        log.debug("Query String    : {}", request.getQueryString());
        log.debug("HTTP Method     : {}", request.getMethod());
        log.debug("Headers         : {}", RequestUtils.getHeadersJson(request));
        log.debug("Cookies         : {}", StringUtils.arrayToCommaDelimitedString(request.getCookies()));
        log.debug("Parameters      : {}", RequestUtils.getParametersJson(request));
        log.debug("Char Encoding   : {}", request.getCharacterEncoding());
        log.debug("Content Type    : {}", request.getContentType());
        log.debug("Request Body    : {}", new String(body, StandardCharsets.UTF_8));
        log.debug("================================request end=========================================");
    }

    private void logResponse(HttpServletResponse response, byte[] body) throws IOException
    {
        log.debug("===============================response begin=======================================");
        log.debug("Status Code     : {}", response.getStatus());
        log.debug("Headers         : {}", ResponseUtils.getHeadersJson(response));
        log.debug("Char Encoding   : {}", response.getCharacterEncoding());
        log.debug("Content Type    : {}", response.getContentType());
        log.debug("Response Body   : {}", new String(body, StandardCharsets.UTF_8));
        log.debug("================================response end========================================");
    }

}
